#ifndef CUTEVM_H
#define CUTEVM_H

#include <QString>
#include <QHash>

#include "VMValue.h"
#include "VMPrim.h"


namespace CuteVM {

    class GlobalStore {
    private:
        QList<ValueHolder>store;
    public:
        GlobalStore() {
            store = QList<ValueHolder>();
        }
    };

    class Environment {
    private:
        QList<ValueHolder> stack;
    public:
        Environment() {
            stack = QList<ValueHolder>();
        }
    };

    class Stack {
    private:
        QList<ValueHolder> stack;
    public:
        Stack() {
            stack = QList<ValueHolder>();
        }

        void push(Value * value) {
            stack.append(ValueHolder(value));
        }

        Value * pop() {
            return stack.takeLast().getValue();
        }
    };

    class Instruction; // forward

    class CodeSegment {
    private:
        QList<Instruction *> instructions;
        QHash<QString,int> labels;
        int entryPoint;
    public:
        CodeSegment() {
            instructions = QList<Instruction*>();
            labels = QHash<QString,int>();
        }
        ~CodeSegment();

        int getEntryPoint() {
            return entryPoint;
        }

        bool hasPC(int pc) {
            return pc>=0 && pc<instructions.size();
        }

        Instruction & fetchInstruction(int pc) {
            if(!hasPC(pc)) {
                qFatal("No instruction at PC=%d",pc);
            }
            return *(instructions[pc]);
        }
    };

    class Primitive; // forward
    class ExecResult; // forward

    class Machine {
    private:
        PrimManager prims;
        CodeSegment code;
        GlobalStore gstore;
        Environment env;
        Stack stack;
        int pc;
    public:
        Machine(PrimManager prims) {
            this->prims=prims;
            gstore = GlobalStore();
            env = Environment();
            stack = Stack();
            pc = 0;
        }

        void load(CodeSegment code) {
            this->code = code;
            pc = code.getEntryPoint();
        }

        int getPC() {
            return pc;
        }

        bool nextPC();

        void push(Value * val) { stack.push(value); }

        Value * pop() { stack.pop(); }
    };

    class ExecError; // forward






} // namespace CuteVM

#endif // CUTEVM_H
